package de.koller.worlders.engine.tiles.data;

import de.koller.oceanFramework.OceanException;
import de.koller.worlders.engine.entity.deco.*;
import de.koller.worlders.engine.entity.fog.Fog;
import de.koller.worlders.engine.entity.fog.Nebula;
import de.koller.worlders.engine.entity.fog.PurpleFog;
import de.koller.worlders.engine.entity.plattform.Catapult;
import de.koller.worlders.engine.entity.plattform.DeathPlattform;
import de.koller.worlders.engine.entity.plattform.SolidPlattform;
import de.koller.worlders.engine.entity.plattform.SpringPlattform;
import de.koller.worlders.engine.entity.zone.*;
import de.koller.worlders.engine.entity.zone.build.BuildZone;
import de.koller.worlders.engine.entity.zone.topdown.BasicFloor;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.TreeMap;

public class TileCreatorGen {
	/* RGBA
	 * A: Grundobject (Plattform, Zone)
	 * R: Tiledarstellung (Abhängig von Grundobject)
	 * G: Decolayer
	 * B: FogLayer
	 */

	private final Map<Integer, Class<?> > base = new TreeMap<>();
	private final Map<Integer, Class<? extends Deco> > deco = new TreeMap<>();
	private final Map<Integer, Class<? extends Fog> > fog = new TreeMap<>();

	public TileCreatorGen() {
		putBaseObject( DeathPlattform.class );
		putBaseObject( SolidPlattform.class );
		putBaseObject( HelpZone.class );
		putBaseObject( ResetZone.class );
		putBaseObject( NegatorZone.class );
		putBaseObject( SpringPlattform.class );
		putBaseObject( ElevatorZone.class );
		putBaseObject( SkyZone.class );
		putBaseObject( NormalizerZone.class );
		putBaseObject( Catapult.class );
		//putBaseObject( Spawner.class );
		putBaseObject( MapLeave.class );
		putBaseObject( Portal.class );
		putBaseObject( ErrorZone.class );
		putBaseObject( BuildZone.class );
		putBaseObject( BasicFloor.class );
		putBaseObject( GravitronZone.class );
		
		putDecoObject( Tree.class );
		putDecoObject( Stone.class );
		putDecoObject( LightBall.class );
		putDecoObject( Torch.class );
		putDecoObject( Gem.class );
		putDecoObject( RollBall.class );
		
		putFogObject( Nebula.class );
		putFogObject( PurpleFog.class );
	}
	
	private void putBaseObject( Class<?> base ) {
		Field f;
		int gid;
		
		try {
			f = base.getField( "GEN_ID" );
		} catch ( NoSuchFieldException ex ) {
			throw new OceanException( "For class " + base + ", there is no GEN_ID field" );
		}
		
		try {
			gid = f.getInt( base );
		} catch ( IllegalAccessException ex ) {
			throw new OceanException( "In class " + base + ", the field GEN_ID have to be public static" );
		}
		
		if( this.base.containsKey( gid ) )
			throw new OceanException( "In class " + base + ", the GEN_ID is already used" );
		if( gid == 0 )
			throw new OceanException( "The class " + base + " uses an reserved gid of 0" );
		
		this.base.put( gid, base );
	}
	
	private void putDecoObject( Class<? extends Deco> deco ) {
		Field f;
		int gid;
		
		try {
			f = deco.getField( "GEN_ID" );
		} catch ( NoSuchFieldException ex ) {
			throw new OceanException( "For class " + deco + ", there is no GEN_ID field" );
		}
		
		try {
			gid = f.getInt( deco );
		} catch ( IllegalAccessException ex ) {
			throw new OceanException( "In class " + deco + ", the field GEN_ID have to be public static" );
		}
		
		if( this.deco.containsKey( gid ) )
			throw new OceanException( "In class " + deco + ", the GEN_ID is already used" );
		if( gid == 0 )
			throw new OceanException( "The class " + deco + " uses an reserved gid of 0" );
		
		this.deco.put( gid, deco );
	}
	
	private void putFogObject( Class<? extends Fog> fog ) {
		Field f;
		int gid;
		
		try {
			f = fog.getField( "GEN_ID" );
		} catch ( NoSuchFieldException ex ) {
			throw new OceanException( "For class " + fog + ", there is no GEN_ID field" );
		}
		
		try {
			gid = f.getInt( fog );
		} catch ( IllegalAccessException ex ) {
			throw new OceanException( "In class " + fog + ", the field GEN_ID have to be public static" );
		}
		
		if( this.fog.containsKey( gid ) )
			throw new OceanException( "In class " + fog + ", the GEN_ID is already used" );
		if( gid == 0 )
			throw new OceanException( "The class " + fog + " uses an reserved gid of 0" );
		
		this.fog.put( gid, fog );
	}

	/** gid is representend by the alpha-value in the mapdata, disp ist the red */
	public ITileCreator getCreator( int gid, int disp ) {
		Class<?> b = base.get( gid );
		if( b == null ) {
			return null;
		}
		
		if( disp == 0 || disp == 0xff ) {
			try {
				return (ITileCreator)b.getField( "GEN_INSTANCE" ).get( b );
			} catch( Exception ex ) {
				throw new OceanException( "The class " + b + " must have an field GEN_INSTANCE" );
			}
		}
		
		try {
			return (ITileCreator)b.getField( "GEN_INSTANCE_" + disp ).get( b );
		} catch( Exception ex ) {
			return null;
		}
	}
	
	public ITileCreator getDecoCreator( int gid ) {
		Class<?> b = deco.get( gid );
		if( b == null ) {
			return null;
		}
		
		try {
			return (ITileCreator)b.getField( "GEN_INSTANCE" ).get( b );
		} catch( Exception ex ) {
			throw new OceanException( "The class " + b + " must have an field GEN_INSTANCE" );
		}
	}

	public ITileCreator getFogCreator( int gid ) {
		Class<?> b = fog.get( gid );
		if( b == null ) {
			return null;
		}
		
		try {
			return (ITileCreator)b.getField( "GEN_INSTANCE" ).get( b );
		} catch( Exception ex ) {
			throw new OceanException( "The class " + b + " must have an field GEN_INSTANCE" );
		}
	}

	public Map<Integer, Class<?>> getBase() {
		return base;
	}

	public Map<Integer, Class<? extends Deco>> getDeco() {
		return deco;
	}

	public Map<Integer, Class<? extends Fog>> getFog() {
		return fog;
	}

	public static void main( String... args ) {
		System.out.println( "Next free IDs: " );
		TileCreatorGen tcg = new TileCreatorGen();
		
		int f = 1;
		do {
			if( !tcg.getFog().containsKey( f ) ) {
				System.out.println( "Free Fog ID: " + f );
				break;
			}
			f++;
		} while( true );
		
		f = 1;
		do {
			if( !tcg.getDeco().containsKey( f ) ) {
				System.out.println( "Free Deco ID: " + f );
				break;
			}
			f++;
		} while( true );
		
		f = 1;
		do {
			if( !tcg.getBase().containsKey( f ) ) {
				System.out.println( "Free Base ID: " + f );
				break;
			}
			f++;
		} while( true );
	}
	
}
